﻿using System;
using System.ComponentModel;
using Gma.UserActivityMonitor;
using System.Windows.Forms;
using System.Drawing;
using VisionarySystems.TimeRegi.Model;

namespace VisionarySystems.TimeRegi.Controller
{
    public enum UseState { Unknown, InUse, Unused } ;

    /// <summary>
    /// Detects if the mouse or keyboard have been used
    /// </summary>
    public class AutomaticRegistrationController
    {
	    #region emums & Delegates
        public delegate void UseStateDelegate (UseState CurrentState, DateTime LastTimeUsed);
        #endregion

        #region Constants

        private const int RequiredPixelsPrSec = 10;
        //private TimeSpan NoMoveTime = new TimeSpan(4, 0, 0); // The time nessasary to detect "Computer not in use" (4 hours)
        
        #endregion

		#region Private members
		// Used for the overall evaluation
		private DateTime m_LastTimeUsed = DateTime.MinValue;
		// Used to check if the mouse has moved (Enough)
		private DateTime m_LastMouseMove = DateTime.MinValue;
		private Point m_LastMouseLocation;
		private UseState m_ProgramUseState = UseState.Unknown;
		private readonly Timer m_NoMoveTimer = new Timer(); // Timer that detects "Unused"
		private readonly ICurrentTimeHandler m_CurrentTimeHandler;
		private readonly TimeSpan m_RegisterNoMoveTime;

		#endregion

        #region Ctor

        public AutomaticRegistrationController(TimeSpan RegisterNoMoveTime, ICurrentTimeHandler CurrentTimeHandler)
        {
	        m_CurrentTimeHandler = CurrentTimeHandler;
	        m_RegisterNoMoveTime = (RegisterNoMoveTime != null) ? RegisterNoMoveTime : new TimeSpan(2, 0, 0);// The time nessasary to detect "Computer not in use" (2 hours)
	        m_LastMouseLocation = new Point(0, 0);
			m_NoMoveTimer.Interval = (int)m_RegisterNoMoveTime.TotalMilliseconds;
            m_NoMoveTimer.Tick += NotInUseDetected;
            m_NoMoveTimer.Start();
	        LogLastTimeUsed(null, null);
            HookEvents();
        }

        public void HookEvents()
        {
	        try
	        {
				// hang on events
				HookManager.MouseMove += CheckMouseHasMoved;
				HookManager.MouseClick += LogLastTimeUsed;
				HookManager.MouseWheel += LogLastTimeUsed;
				HookManager.KeyPress += LogLastTimeUsed;
	        }
			catch (Win32Exception e)
	        {
		        
	        }
            
        }

        private void NotInUseDetected(object sender, EventArgs e)
        {
            ProgramUseState = UseState.Unused; // Set the property (And fire event on change)
        }

        #endregion

        #region Properties

        /// <summary>
        /// Holds the value of the ProgramUseState
        /// If the state changes, the event "ProgramUseStateChanged" is fired
        /// </summary>
        public UseState ProgramUseState
        {
            get { return m_ProgramUseState; }
            set
            {
                if (value != m_ProgramUseState)
                {
                    m_ProgramUseState = value;
                    if (ProgramUseStateChanged != null)
                        ProgramUseStateChanged(m_ProgramUseState, LastTimeUsed);
                }
            }
        }

        public DateTime LastTimeUsed 
        {
            get { return m_LastTimeUsed; }
        } 

        #endregion

		#region Event

		public event UseStateDelegate ProgramUseStateChanged;

		#endregion

		#region Private methods

        private void CheckMouseHasMoved(object sender, MouseEventArgs e)
        {
			// Get time since last move
			TimeSpan timeSinceMove = GetTimeSinceLastMove();

			// Get move in pixels
            double moveSize = GetMoveInPixels(e);
            
            // Check if the mouse has moved
            if (HasMouseMoved(moveSize, timeSinceMove))
            {
                LogLastTimeUsed(sender, e);
            }
			m_LastMouseMove = m_CurrentTimeHandler.CurrentTime;
        }

	    private int GetMoveInPixels(MouseEventArgs e)
	    {
		    return Math.Max(Math.Abs(m_LastMouseLocation.X - e.Location.X), Math.Abs(m_LastMouseLocation.X - e.Location.X));
	    }

	    private TimeSpan GetTimeSinceLastMove()
	    {
		    return m_CurrentTimeHandler.CurrentTime.Subtract(m_LastMouseMove);
	    }

	    private bool HasMouseMoved(double moveSize, TimeSpan timeSinceMove)
	    {
		    return (moveSize / timeSinceMove.TotalSeconds) > RequiredPixelsPrSec;
	    }

        private void LogLastTimeUsed(object sender, EventArgs e)
        {
            m_LastTimeUsed = m_CurrentTimeHandler.CurrentTime;
            ProgramUseState = UseState.InUse; // Set the property (And fire event on change)
            ResetNoMoveTimer();
        }

	    private void ResetNoMoveTimer()
	    {
		    m_NoMoveTimer.Stop();
		    m_NoMoveTimer.Start();
	    }

	    #endregion
    }
}
